package com.apexsoft.monitor.ssh.util;


import com.alibaba.fastjson.JSONObject;
import com.apexsoft.front.common.support.Result;
import com.apexsoft.front.common.utils.CryptUtil;
import com.apexsoft.monitor.front.console.host.om.Host;
import com.jcraft.jsch.*;
import com.jcraft.jsch.ChannelSftp.LsEntry;
import org.apache.logging.log4j.LogManager;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Vector;

/**
 * Shh 工具
 */
public class ShellUtils {
    private static final org.apache.logging.log4j.Logger log = LogManager.getRootLogger();
    private String localPath =  "";
    private String localFile = "magent"; //发送的本地文件名
    private Host host;
    private JSch jsch;
    private Session session;
    private static int sshTimeOut = 30000;
    private static int sshTimeOutTest = 15000; //测试连接超时时间,因页面上测试需要即时返馈,所以设置1秒钟.

    /**
     * 构造函数
     */
    public ShellUtils(Host host){
        super();
        this.host = host;

    }

    /**
     * 连接到指定的IP
     * @return
     */
    public JSONObject connect(){
        return connect("");
    }
    /**
     * 连接到指定的IP
     * @throws JSchException
     */
    public JSONObject connect(String type){
        JSONObject result = new JSONObject();
        try{
            jsch = new JSch();
            session = jsch.getSession(this.host.getUserName(),this.host.getHostIp(),this.host.getHostPort());
            session.setPassword(CryptUtil.decode(this.host.getUserPwd(),"apexsoft"));
            java.util.Properties config = new java.util.Properties();
            config.put("userauth.gssapi-with-mic", "no");
            config.put("StrictHostKeyChecking", "no"); //跳过主机Key检测
            if(type.equals("test")){
                session.setTimeout(sshTimeOutTest);
            }else{
                session.setTimeout(sshTimeOut);
            }
            session.setConfig(config);
            session.connect();
            result.put(Result.CODE, Result.SC_0001);
            result.put(Result.NOTE, "连接成功");
        }catch (Exception e){
            log.error("连接服务器失败："+this.host.getHostIp()+":"+this.host.getHostPort()+":"+this.host.getUserName()+":"+this.host.getUserPwd()+e.getMessage(), e);
            closeSession();
            result.put(Result.CODE, Result.EC_1000);
            result.put(Result.NOTE, e.getMessage());
        }
        return result;
    }


    /**
     * 关闭Session
     */
    public void closeSession (){
        if(session!=null)session.disconnect();
    }

    /**
     * 递归生成目录
     * @param path
     */
    public void mkdir(ChannelSftp sftp,String path) throws SftpException{
        String[] folders = path.split( "/" );
        boolean isFirst = true;
        for ( String folder : folders ) {
            if ( folder.length() > 0 ) {
                try {
                    if(isFirst){
                        sftp.cd( "/"+folder );
                        isFirst = false;
                    }else{
                        sftp.cd( folder );
                    }
                } catch ( SftpException e ) {
                    sftp.mkdir( folder );
                    sftp.cd( folder );
                }
            }
        }
    }

    /**
     * 发送文件到指定的服务器目录下
     * @param path
     * @throws JSchException
     */
    public void putFile(String path) throws JSchException {
        this.localPath = Thread.currentThread().getContextClassLoader().getResource("monitor/"+this.localFile).getPath();
        if (new File(System.getProperty("user.dir") + "/config/monitor/"+this.localFile).exists()) {
            this.localPath = System.getProperty("user.dir") + "/config/monitor/"+this.localFile;
            log.info("load magent filePath:" + this.localPath);
        } else {
            log.info("load default magent filePath:"+this.localPath);
        }
        ChannelSftp channel = null;
        try {
            channel=(ChannelSftp)session.openChannel("sftp");
            channel.connect();
            //如果没找到目录,就创建目录,并定位到该目录
            if (!path.isEmpty()){
                Vector<LsEntry> conVector = null;
                this.mkdir(channel,path);
                conVector = channel.ls(path);
                boolean isHave = false;
                for(LsEntry entry:conVector){
                    if(entry.getFilename().equals(this.localFile)){
                        isHave = true;
                        break;
                    }
                }
                channel.cd(path);

                //如果没找到文件就重新pull文件,有找到文件,判断是否要覆盖.
                if(isHave){
                    channel.rm(this.localFile);
                }
                channel.put(this.localPath, this.localFile); //第二个参数为发送的目标文件名
            }
        } catch (Exception e) {
            throw new JSchException(e.getMessage());
        }  finally {
            if(channel!=null)
                channel.disconnect();
        }
    }

    /**
     * 睡眠
     * @param channel
     */
    private void waitForChannelClosed(Channel channel) {
        try {
            while(!channel.isEOF()) {
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 执行相关的命令
     * separate : 分隔符
     * @throws JSchException
     */
    private void readAll(BufferedReader reader, StringBuffer result, String separate) {
        try{
            String buf = null;
            while ((buf = reader.readLine()) != null) {
                byte[] bytes = buf.getBytes();
                String tempContent = new String(bytes);
                result.append(tempContent.trim() + separate);
            }
            reader.close();
        }catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 执行命令
     * @param command
     * @param separate  返回数据行尾的标志( \n 或 <br>)
     * @return
     * @throws JSchException
     */
    public JSONObject execCmdWithEnv(String command,String separate) throws JSchException {
        JSONObject result = new JSONObject();
        Channel channel = null;
        StringBuffer commandRusult = new StringBuffer();
        try {
            if (command != null) {
                channel = session.openChannel("exec");
                ChannelExec execChannel = (ChannelExec) channel;
                execChannel.setCommand(command);
                channel.setInputStream(null);
                InputStream stdout = channel.getInputStream();
                InputStream stderr = ((ChannelExec) channel).getErrStream();
                channel.connect();
                waitForChannelClosed(channel);
                if (channel.getExitStatus() != 0) {
                    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(stderr));
                    readAll(bufferedReader, commandRusult, separate);
                    throw new Exception(commandRusult.toString());
                } else {
                    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(stdout));
                    readAll(bufferedReader, commandRusult, separate);
                }
                result.put(Result.CODE, Result.SC_0001);
                result.put(Result.NOTE, commandRusult.toString());
            }else{
                result.put(Result.CODE, Result.EC_1000);
                result.put(Result.NOTE, "not command statement");
            }
        }catch (Exception e){
            result.put(Result.CODE, Result.EC_1000);
            result.put(Result.NOTE, e.getMessage());
            e.printStackTrace();
        }finally {
            if(channel!=null){
                channel.disconnect();
            }
        }
        return result;
    }

    /**
     * 执行命令
     */
    public JSONObject execCmd(String command) throws JSchException {
        return execCmdWithEnv(command, "\n");
    }
}
